/*
   Copyright 2010-2012 Portland Transport

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

var transitBoardByLine = {}; // keep state

transitBoardByLine.paging_state = {}; // paging state
transitBoardByLine.paging_state.next_row = undefined;
transitBoardByLine.paging_state.page_number = 0;
transitBoardByLine.standing_messages = new Array;
transitBoardByLine.connection_health = 1;
transitBoardByLine.service_messages = [];
transitBoardByLine.minutes_limit = 0;
transitBoardByLine.arrivals_limit = 0;
transitBoardByLine.rotation_complete = true;
transitBoardByLine.isChumby = navigator.userAgent.match(/QtEmb/) != null;
transitBoardByLine.animation_factor = 0.85; // arbitrary value to allow for pause time plus javascript processing time, will be dynamically adjusted
transitBoardByLine.messages = [];
transitBoardByLine.start_time = new Date();
transitBoardByLine.car2go = 0;
transitBoardByLine.suppress_scrolling = false;
transitBoardByLine.suppress_downtown_only = false;

transitBoardByLine.isotope_container = null;

transitBoardByLine.standing_messages.push("<span>TransitBoard&trade; is a product of Portland Transport.</span>");
transitBoardByLine.standing_messages.push("<span>Learn more at http://transitappliance.com</span>");

transitBoardByLine.formatted_arrival_time = function(arrival) {
	var displayTime = "";
	var milliseconds_until_arrival = arrival.arrivalTime - new Date();
	
	var minutes_until_arrival = Math.round(milliseconds_until_arrival/60000);
	if (minutes_until_arrival == 0) {
		minutes_until_arrival = "Due";
	} else {
		minutes_until_arrival = "<nobr>"+minutes_until_arrival+" min</nobr>"; 
	}
	if (arrival.type == 'scheduled') {
		timeclass = ' scheduled';
		var sched_date = localTime(arrival.arrivalTime);
		displayTime = sched_date.toString('h:mmtt');
		displayTime = displayTime.replace(/^0:/,'12:');
	} else {
		displayTime = minutes_until_arrival;
		timeclass = "";
	}
	
	return displayTime;
}

transitBoardByLine.resetMessageQueue = function() {
	transitBoardByLine.messages = [];
	for (var i = 0; i < transitBoardByLine.standing_messages.length; i++) {
		transitBoardByLine.messages.push(transitBoardByLine.standing_messages[i]);
	}
	// raw score: messages.push('<span style="font-weight: bold; color: red">['+transitBoardByLine.connection_health+']</span>');
	if (transitBoardByLine.connection_health < 0.2) {
		transitBoardByLine.messages.push('<span style="font-weight: bold; color: red">This display has lost connectivity.</span>');
	} else if (transitBoardByLine.connection_health < 0.5) {
		transitBoardByLine.messages.push('<span style="font-weight: bold; color: red">This display is experencing severe connection issues.</span>');
	} else if (transitBoardByLine.connection_health < 0.8) {
		transitBoardByLine.messages.push('<span style="font-weight: bold; color: red">This display is experencing connection issues.</span>');
	}
	for (var i = 0; i < transitBoardByLine.service_messages.length; i++) {
		transitBoardByLine.messages.push('<span style="font-weight: bold; color: red">'+transitBoardByLine.service_messages[i]+'</span>');
	}
	var dimensions = jQuery(window).width()+"x"+jQuery(window).height()
  transitBoardByLine.messages.push("<span style=\"font-size: 60%\">["+transitBoardByLine.appliance_id+" "+dimensions+" "+transitBoardByLine.animation_factor+"]</span>");
}

transitBoardByLine.advanceMessage = function() {
	if (transitBoardByLine.messages.length != 0) {
		var message = transitBoardByLine.messages.shift();
		jQuery("div.scroller").fadeOut("slow",function() {
			jQuery("div.scroller").html("<table><tr><td align=\"center\">"+message+"</td></tr></table>");
			jQuery("div.scroller").fadeIn("slow");
		});
	}
	if (transitBoardByLine.messages.length == 0) {
		transitBoardByLine.resetMessageQueue();
	}
}

transitBoardByLine.initializePage = function(data) {	
	
	// check for Chumby screen resolutions and mark a class in the body tag
	if ( transitBoardByLine.isChumby ) {
		//jQuery("body").addClass("chumby");
	}
	
	if (data.optionsConfig != undefined && data.optionsConfig.lat != undefined && data.optionsConfig.lat[0] != undefined) {
		if (data.optionsConfig.lng != undefined && data.optionsConfig.lng[0] != undefined) {
			if (data.optionsConfig.car2go != undefined && data.optionsConfig.car2go[0] != undefined) {
				transitBoardByLine.car2go = data.optionsConfig.car2go[0];
				if (transitBoardByLine.car2go != 0 ) {
					transitBoardByLine.cars = new trCar2Go({
						lat: data.optionsConfig.lat[0],
						lng: data.optionsConfig.lng[0],
						loc: 'portland',
						consumer_key: 'TransitAppliance',
						num_vehicles: transitBoardByLine.car2go
					});
				}
			}
		}
	}
	

	// kill the logging element
	jQuery("#arrivals_log_area").remove();
	
	transitBoardByLine.displayInterval = data.displayInterval;
	
	
	if (data.applianceConfig != undefined && data.applianceConfig.id != undefined && data.applianceConfig.id[0] != undefined) {
		transitBoardByLine.appliance_id = data.applianceConfig.id[0];
	} else {
		transitBoardByLine.appliance_id = "Unassigned";
	}
	
	if (data.optionsConfig.banner != undefined && data.optionsConfig.banner[0] != undefined) {
		var banner = data.optionsConfig.banner[0];

		if (banner.substr(0,1) == '*') {
			document.title = banner.substr(1);
		} else {
			document.title = "Transit Board(tm) for "+banner;
		}
		transitBoardByLine.banner = data.optionsConfig.banner[0];
	} else {
		transitBoardByLine.banner = "";
	}
	
	if (data.optionsConfig.suppress_scrolling != undefined && data.optionsConfig.suppress_scrolling[0] != undefined && data.optionsConfig.suppress_scrolling[0] != "") {
		transitBoardByLine.suppress_scrolling = true;
	}
	
	if (data.optionsConfig.suppress_downtown_only != undefined && data.optionsConfig.suppress_downtown_only[0] != undefined && data.optionsConfig.suppress_downtown_only[0] != "") {
		transitBoardByLine.suppress_downtown_only = true;
	}
	
	if (data.optionsConfig.minutes_limit != undefined && data.optionsConfig.minutes_limit[0] != undefined && data.optionsConfig.minutes_limit[0] != 0) {
		transitBoardByLine.minutes_limit = data.optionsConfig.minutes_limit[0];
	}
	if (transitBoardByLine.minutes_limit == 0) {
		transitBoardByLine.minutes_limit = 60;
	}
	
	if (data.optionsConfig.arrivals_limit != undefined && data.optionsConfig.arrivals_limit[0] != undefined && data.optionsConfig.arrivals_limit[0] != 0) {
		transitBoardByLine.arrivals_limit = data.optionsConfig.arrivals_limit[0];
	}
	
	if (data.optionsConfig['split-by-direction'] != undefined && data.optionsConfig['split-by-direction'][0] != undefined && data.optionsConfig['split-by-direction'][0] != 0) {
		transitBoardByLine.split_by_direction = true;
	} else {
		transitBoardByLine.split_by_direction = false;
	}
	
	if (data.optionsConfig.columns != undefined && data.optionsConfig.columns[0] != undefined && data.optionsConfig.columns[0] != 0) {
		transitBoardByLine.columns = data.optionsConfig.columns[0];
	} else {
		transitBoardByLine.columns = 2; // default
	}
	
	if (data.optionsConfig.ping != undefined && data.optionsConfig.ping[0] != undefined) {
		transitBoardByLine.ping = function() {		
			jQuery.ajax({
  			url: "assets/img/ping.png",
  			cache: false
			});
			setTimeout("transitBoardByLine.ping()",data.optionsConfig.ping[0]*1000);
		}
		// create a ping (to keep internet connections open) every specified number of seconds
		transitBoardByLine.ping();
	}
	
	// add stylesheet

	if (data.optionsConfig.stylesheet != undefined && data.optionsConfig.stylesheet[0] != undefined) {
		var link = jQuery("<link>");
		link.attr({
			type: 'text/css',
		  rel: 'stylesheet',
		  href: data.optionsConfig.stylesheet[0]
		});
		jQuery("head").append( link ); 
		
	}
	
	if (data.optionsConfig.logo != undefined && data.optionsConfig.logo[0] != undefined && data.optionsConfig.logo[0] != "") {
		var logo = '<img src="'+data.optionsConfig.logo[0]+'">';
	} else {
		var logo = '';
	}
	

	var font_scale_factor = 1;
	if (data.optionsConfig['font-size-adjust'] != undefined && data.optionsConfig['font-size-adjust'][0] != undefined) {
		font_scale_factor = data.optionsConfig['font-size-adjust'][0]/100;
	}
	
	// set sizes
	var window_height = jQuery(window).height();
	var basic_text = Math.floor(font_scale_factor*window_height/30) + "px";
	var large_text = Math.floor(font_scale_factor*window_height/20) + "px";
	var padding    = Math.floor(font_scale_factor*window_height/100) + "px";
	var scroller_height = (Math.floor(font_scale_factor*window_height/30)+Math.floor(font_scale_factor*window_height/100)) + "px";
	
	// bigger fonts for wider displays
	if (jQuery(window).width()/jQuery(window).height() > 1.4) {
		window_height = jQuery(window).height();
		basic_text = Math.floor(font_scale_factor*window_height/22) + "px";
		large_text = Math.floor(font_scale_factor*window_height/14) + "px";
		padding    = Math.floor(font_scale_factor*window_height/100) + "px";
		scroller_height = (Math.floor(font_scale_factor*window_height/22)+Math.floor(font_scale_factor*window_height/100)) + "px";
	}
	

	jQuery("head").append(jQuery('\
		<style>\
			#tb_bottom td { font-size: '+basic_text+';}\
			h1 { font-size: '+large_text+'; margin-bottom: '+padding+'; }\
			body { overflow: hidden }\
		</style>\
	'));
	
	// get the rights strings
	for (var agency in data.stopsConfig) {
		transitBoardByLine.standing_messages.push("<span>"+data.agencyCache.agencyData(agency).rights_notice+"</span>");
	}
	transitBoardByLine.standing_messages.push("<span>HH:MM = scheduled arrival, real-time estimate unavailable.</span>");
		
	// populate html
	
	var html = '\
<div id="tb_top">\
	';
	if ((logo != "") || (transitBoardByLine.banner != "")) {
		html += '\
<table cellpadding="10"><tr valign="middle">\
		';
		if (logo != "") {
			html += '<td id="logo" align="center">'+logo+'</td>';
		}
		if (transitBoardByLine.banner.substr(0,1) == "*") {
			html+= '<td id="banner" width="100%" align="center"><h1>'+transitBoardByLine.banner.substr(1)+'</h1></td>';
		} else if (transitBoardByLine.banner != "") {
			html+= '<td id="banner" width="100%" align="center"><h1>Transit Board&trade; for '+transitBoardByLine.banner+'</h1></td>';
		}
		html += '\
</tr></table>\
		';
	}
	html += '\
</div>\
<div id="tb_middle">\
	<div id="arrivals_outer_wrapper">\
		<div id="arrivals_inner_wrapper">\
		</div>\
	</div>\
</div>\
<table id="tb_bottom"><tr><td id="tb_clock"></td><td id="tb_ticker"><div class="scroller"><div class="scrollingtext"></div></div></td></tr></table>\
	';
	
	jQuery('body').html(html);
		
	// add test div to work out widths
	
	jQuery('#arrivals_inner_wrapper').html('\
		<div id="wrapper1">\
		<table id="trip1" class="trip_wrapper isotope-item" data-sortkey="50040">\
			<tbody id="3639_4DivisiontoGreshamTCA" class="trip service_color_yellow">\
				<tr valign="middle">\
					<td class="route"><span>MAXi</span></td>\
					<td class="destination"><div>Division to <span class="terminus">Gresham TC</span> from SW Madison &amp; 4th and tack on some very long text that is bound to overflow if we keep adding more and more and more and more of it</div></td>\
					<td class="arrivals">\
							<span>10:08AMa<br>\
							<span class="second_arrival">2 min</span><span>\
					</td>\
				</tr>\
			</tbody>\
		</table>\
		</div>\
		<div id="wrapper2">\
		<table id="trip2" class="trip_wrapper isotope-item" data-sortkey="50050">\
			<tbody id="3639_4DivisiontoGreshamTCB" class="trip service_color_yellow">\
				<tr valign="middle">\
					<td class="route"><span>MAXi</span></td>\
					<td class="destination"><div>Division to <span class="terminus">Gresham TC</span> from SW Madison &amp; 4th and tack on some very long text that is bound to overflow if we keep adding more and more and more and more of it</div></td>\
					<td class="arrivals">\
							<span>10:08AMa<br>\
							<span class="second_arrival">2 min</span><span>\
					</td>\
				</tr>\
			</tbody>\
		</table>\
		</div>\
	');
	

	var trip_scale_factor = 1;
	if (data.optionsConfig['trip-size-adjust'] != undefined && data.optionsConfig['trip-size-adjust'][0] != undefined) {
		trip_scale_factor = data.optionsConfig['trip-size-adjust'][0]/100;
	}
	
	var base_em_size = parseFloat(jQuery("table.trip_wrapper").css("font-size"),10);
	base_em_size = (base_em_size*trip_scale_factor);
	// create style section with new size
	
	jQuery("head").append(jQuery('\
		<style>\
			table.trip_wrapper { font-size: '+base_em_size+'px; }\
		</style>\
	'));	
	
 	
	var bottom_height = jQuery(window).height() - jQuery("#tb_bottom").offset().top;

	jQuery("body").css("padding-bottom",bottom_height+"px");
	
	// minimize width of route and arrival elements
	var route_cell_width = jQuery("#trip1 td.route").width();
	var route_text_width = jQuery("#trip1 td.route span").width();
	var arrivals_text_width = jQuery("#trip1 td.arrivals span").width();
	//alert(route_cell_width+" "+route_text_width);
	
	transitBoardByLine.target_width = Math.floor(jQuery("#tb_middle").width()/transitBoardByLine.columns);
	
	jQuery("head").append(jQuery('\
		<style>\
		table.trip_wrapper { width: '+transitBoardByLine.target_width+'px; }\
			table.trip_wrapper tbody tr td.route { min-width: '+route_text_width+'px !important; }\
			table.trip_wrapper tbody tr td.arrivals { min-width: '+arrivals_text_width+'px !important; }\
			table.trip_wrapper tbody tr td.destination { width: 100%; }\
		</style>\
	'));

	setTimeout( function() {
		transitBoardByLine.target_width = Math.floor(jQuery("#tb_middle").width()/transitBoardByLine.columns);
		var actual_width = jQuery("tbody.trip").outerWidth(true);
			
		var destination_wrapper_width = jQuery("table.trip_wrapper tbody tr td.destination").width() + transitBoardByLine.target_width - actual_width;
				
		// mark sure the top margin is an integer
		var margin = parseInt(jQuery("table.trip_wrapper").css("margin-top"),10);
		margin = Math.floor(margin+0);
	
		jQuery("head").append(jQuery('\
			<style>\
				/*table.trip_wrapper tbody tr td.destination { width: '+destination_wrapper_width+'px; }*/\
				table.trip_wrapper { height: '+jQuery('#trip2').height()+'px; width: '+transitBoardByLine.target_width+'px; margin-top: '+margin+'px; }\
			</style>\
		'));
		
		// set up scroller
	
		var cell_width = jQuery("#tb_ticker").width();
		jQuery(".scroller").css("height",scroller_height);
		
		setTimeout(function(){
			// allow html to settle before calculating heights
		
			var trip_height = jQuery('#trip2').outerHeight(true);
			transitBoardByLine.trip_height = trip_height;
			transitBoardByLine.max_available_height = jQuery("#tb_bottom").offset().top - jQuery("#tb_middle").offset().top - 20;
			transitBoardByLine.trips_per_screen = Math.floor(transitBoardByLine.max_available_height/trip_height);
			transitBoardByLine.max_available_height = transitBoardByLine.trips_per_screen*trip_height;
			transitBoardByLine.animation_row_step = Math.ceil(transitBoardByLine.trips_per_screen/3);
			transitBoardByLine.animation_step = Math.ceil(transitBoardByLine.trips_per_screen/3)*trip_height;

			
			// set the height of the div
			jQuery("#tb_middle").css("height",transitBoardByLine.max_available_height+"px").css("width","100%");
			
			// kill the test divs
			jQuery("#wrapper1,#wrapper2").remove();


		},2000);
		
	},2000);
	
}



transitBoardByLine.do_animation_step = function(animation_step_time) {
	var trip_keys = {};
	var trip_ids = [];
	jQuery(".trip_wrapper").each(function(index,item){
		trip_ids.push(jQuery(item).children("tbody").attr("id"));
		trip_keys[jQuery(item).children("tbody").attr("id")] = jQuery(item).attr("data-sortkey");
	});
	trip_ids.sort(function(a, b) {
    return parseInt(trip_keys[a]) < parseInt(trip_keys[b]) ? -1 : (parseInt(trip_keys[a]) > parseInt(trip_keys[b]) ? 1 : 0);
	});
	//debug_alert(trip_keys);
	
	var last = true;
	var rows = Math.ceil(trip_ids.length/transitBoardByLine.columns);
	var step_count = transitBoardByLine.animation_row_step * transitBoardByLine.columns;
	var i = 0;
	var updated_elements = [];
	var updated_ids = [];
	while (i < step_count) {
		var trip_id = trip_ids[i];
		var order = jQuery('#'+trip_id).parent().attr("data-sortkey");
		//alert(order);
		if (order < 1000000000) {
			jQuery('#'+trip_id).parent().attr("data-sortkey",order*1+1000000000);
			//jQuery('#'+trip_id+" td.destination").html(jQuery('#'+trip_id).parent().attr("data-sortkey"));
			updated_elements.push(jQuery('#'+trip_id).parent());
			updated_ids.push(trip_id);
			last = false;
		}
		i++;
	}
	//debug_alert(updated_ids);

	transitBoardByLine.isotope_container.isotope('updateSortData',jQuery(".trip_wrapper")).isotope('reLayout').isotope();
	
	setTimeout(function() {
		if (last) {
			var i = 0;
			while (i < trip_ids.length) {
				trip_id = trip_ids[i];
				var order = jQuery('#'+trip_id).parent().attr("data-sortkey");
				if (order > 1000000000) {
					jQuery('#'+trip_id).parent().attr("data-sortkey",order*1-1000000000);
					//jQuery('#'+trip_id+" td.destination").html(jQuery('#'+trip_id).parent().attr("data-sortkey"));
				}
				i++;
			}
			transitBoardByLine.rotation_complete = true;
		} else {
			setTimeout(function() {
				transitBoardByLine.do_animation_step(animation_step_time);
			},animation_step_time*3);
			transitBoardByLine.advanceMessage();
		}
	},animation_step_time);
}

transitBoardByLine.animate_display = function() {
	var trip_ids = [];
	jQuery(".trip_wrapper").each(function(index,item){
		trip_ids.push(jQuery(item).children("tbody").attr("id"));
	});
	var i = 0;
	while (i < trip_ids.length) {
		trip_id = trip_ids[i];
		var order = jQuery('#'+trip_id).parent().attr("data-sortkey");
		if (order > 1000000000) {
			jQuery('#'+trip_id).parent().attr("data-sortkey",order*1-1000000000);
			//jQuery('#'+trip_id+" td.destination").html(jQuery('#'+trip_id).parent().attr("data-sortkey"));
		}
		i++;
	}
	var animation_start = new Date();
	if (transitBoardByLine.isotope_container.height() > transitBoardByLine.max_available_height && !transitBoardByLine.suppress_scrolling) {
		if (transitBoardByLine.rotation_complete) {
			transitBoardByLine.rotation_complete = false;
			//jQuery(".trip_wrapper").width(transitBoardByLine.target_width);
			var animation_step_time = Math.floor((transitBoardByLine.animation_factor*(transitBoardByLine.displayInterval)/4)*transitBoardByLine.animation_step/transitBoardByLine.isotope_container.height());

			setTimeout(function() {
				transitBoardByLine.do_animation_step(animation_step_time);
			},2000); // initial two second delay in starting animation
		} else {
			//fell behind, so we don't animate stops
			if (transitBoardByLine.animation_factor > 0.6) {
				transitBoardByLine.animation_factor = transitBoardByLine.animation_factor * 0.95; // speed things up a bit
			}
		}
	} else {
		// need to rotate message
		var message_interval = transitBoardByLine.displayInterval/4;
		// 3 times
		transitBoardByLine.advanceMessage();
		setTimeout(function() {
			transitBoardByLine.advanceMessage();
			setTimeout(function() {
				transitBoardByLine.advanceMessage();
			},message_interval);
		},message_interval);
	}
}

transitBoardByLine.displayPage = function(data, callback) {
	
	var running_time = new Date() - transitBoardByLine.start_time;
	var running_minutes = Math.floor(running_time/(60*1000));
	var client_time = localTime();
	
	if (running_minutes > 65 && client_time.getHours() == 3 && transitBoardByLine.isChumby) {
		location.reload();
	}
	
	if (data.displayCallCount == -1) {
		if (callback) {
			callback();
		}
		return;
	}
		
	transitBoardByLine.isotope_container = jQuery('#arrivals_inner_wrapper');
	transitBoardByLine.isotope_container.isotope(
		{
		  // options
		  animationEngine: 'best-available',
		  //transformsEnabled: !transitBoardByLine.isChumby,
		  transformsEnabled: true,
		  itemSelector : 'table.trip_wrapper',
		  layoutMode: 'masonry',  
			getSortData : {
			  sortkey : function ( $elem ) {
			    return parseInt($elem.attr('data-sortkey'));
			  }
			},
			sortBy : 'sortkey'
		}
	);
			
	// we finished paging sequence previously, need to build a new page state
	
	var by_trip = {};
	
	var filtered_queue = filter_queue(data.arrivalsQueue);
	
	for (var i = 0; i < filtered_queue.length; i++) {
		
		var trip_identifier = filtered_queue[i].stop_id+"_"+filtered_queue[i].headsign.replace(/[^a-zA-Z0-9]/g,"");
		
		if (filtered_queue[i].headsign.substr(0,3) == 'MAX') {
			filtered_queue[i].app_route_id = "MAX";
			filtered_queue[i].app_headsign_less_route = filtered_queue[i].headsign.replace(/^MAX /,"");
			filtered_queue[i].app_color = filtered_queue[i].app_headsign_less_route.replace(/ .*$/,"").toLowerCase();
		} else if (filtered_queue[i].route_id == "193") {
			filtered_queue[i].app_route_id = "NS";
			filtered_queue[i].app_headsign_less_route = "<b>Streetcar</b> "+filtered_queue[i].headsign.replace("NS Line","");		
			filtered_queue[i].app_color = 'streetcar'
		} else if (filtered_queue[i].route_id == "194") {
			filtered_queue[i].app_route_id = "CL";
			filtered_queue[i].app_headsign_less_route = "<b>Streetcar</b> "+filtered_queue[i].headsign.replace("CL Line","");		
			filtered_queue[i].app_color = 'streetcar'
		} else {		
			var route_name = filtered_queue[i].route_data.route_short_name || filtered_queue[i].route_data.route_long_name;
			filtered_queue[i].app_route_id = route_name;
			filtered_queue[i].app_headsign_less_route = filtered_queue[i].headsign.replace(route_name,"");
			filtered_queue[i].app_color = filtered_queue[i].route_data.service_class;
		}
		
		// highlight terminus
		if (filtered_queue[i].app_headsign_less_route.match(/ to /i)) {
			filtered_queue[i].app_headsign_less_route = filtered_queue[i].app_headsign_less_route.replace(/ to /i," to <span class=\"terminus\">")+"</span>";
		}
		
		/* logic for sort key
		
			for transit:
		
			crrrd
			
			c = service class
			rrr = route (3 digit max)
			d = direction (1 or 0)
			
			for car2go:
			
			9ddd0
			
			ddd= distance in 10th of mile units (multiply by 10, take floor)
		
		
		
		*/
		
		if (!by_trip[trip_identifier]) {
			by_trip[trip_identifier] = {};
			by_trip[trip_identifier].arrivals = [];
			by_trip[trip_identifier].stop_id = filtered_queue[i].stop_id;
			by_trip[trip_identifier].first_arrival_time = filtered_queue[i].arrivalTime;
			var service_class = filtered_queue[i].route_data.service_class;
			if (service_class > 4) {
				service_class = 5;
			}
			var direction_multiplier = 0;
			if (transitBoardByLine.split_by_direction) {
				direction_multiplier = 100000;
			} else {
				direction_multiplier = 1;
			}
			by_trip[trip_identifier].sort_key = 10*filtered_queue[i].route_id + 10000*service_class + direction_multiplier*filtered_queue[i].route_data.direction_id;
		}
		by_trip[trip_identifier].arrivals.push(filtered_queue[i]);
	}
	

	
	function trArrCompareArrivalSets(a,b) {
		return +by_trip[a].sort_key - +by_trip[b].sort_key;
	}
	

	var trip_keys = [];
	for (var key in by_trip) {
		trip_keys.push(key);
	}
	
	var sorted_trip_keys = trip_keys.sort(trArrCompareArrivalSets);
	
	var trip_objects = {};
	var trip_inner_html = {};
	var trip_arrivals_html = {};
	for (var i = 0; i < sorted_trip_keys.length; i++) {
		var trip_key = sorted_trip_keys[i];
		var trip_arrival = transitBoardByLine.formatted_arrival_time(by_trip[trip_key].arrivals[0])+"<br><span class=\"second_arrival\">";
		if (by_trip[trip_key].arrivals[1]) {
			trip_arrival += transitBoardByLine.formatted_arrival_time(by_trip[trip_key].arrivals[1])+"</span>";
		} else {
			trip_arrival += "&nbsp;</span>";
		}
		
		var trip_inner = '<tr valign="middle"><td class="route">'+by_trip[trip_key].arrivals[0].app_route_id+"</td>\n";
		trip_inner += '<td class="destination" valign="middle"><div>'+by_trip[trip_key].arrivals[0].app_headsign_less_route+" from "+by_trip[trip_key].arrivals[0].stop_data.stop_name+"</div></td>\n";
		trip_inner += "<td class=\"arrivals\">"+trip_arrival+"</td></tr>";
		

		var by_trip_html = "<table class=\"trip_wrapper\" data-sortkey=\""+by_trip[trip_key].sort_key+"\"><tbody id=\""+trip_key+"\" class=\"trip service_color_"+by_trip[trip_key].arrivals[0].app_color+" route_"+by_trip[trip_key].arrivals[0].route_id+" direction_"+by_trip[trip_key].arrivals[0].route_data.direction_id+"\">\n";
		by_trip_html += trip_inner+"</tbody></table>";
		
		trip_objects[trip_key] = by_trip_html;
		trip_inner_html[trip_key] = trip_inner;
		trip_arrivals_html[trip_key] = trip_arrival;
	}			
	
	// now do spacers to get balanced columns
	
	var display_elements = sorted_trip_keys.length;
	var remainder = display_elements % transitBoardByLine.columns;

	if (remainder > 0) {
		var spacer_inner = '\
					<tr valign="middle">\
						<td class="route"></td>\
						<td class="destination"><div></div></td>\
					</tr>\
		';
			
		for (i=1;i<=remainder;i++) {
			var spacer = '\
					<table class="trip_wrapper service_color_spacer spacer spacer'+i+' isotope-item" data-sortkey="95000">\
						<tbody id="spacer'+i+'" class="trip service_color_spacer">\
						'+spacer_inner+'\
						</tbody>\
					</table>\
			';	
			var spacer_id = 'spacer'+i;			
			trip_objects[spacer_id] = spacer;
			trip_inner_html[spacer_id] = spacer_inner;
			sorted_trip_keys.push(spacer_id);
		}
	}
	
	var insertion_queue = [];
	var removal_queue = [];
	
	function process_insertions() {
		if (insertion_queue.length > 0) {
			var obj = jQuery(insertion_queue.shift());
			//transitBoardByLine.isotope_container.append(obj);
			transitBoardByLine.isotope_container.isotope( 'insert', obj );
			process_insertions();
		} else {
				
			//transitBoardByLine.isotope_container.isotope('reLayout').isotope();
			setTimeout(function(){
				transitBoardByLine.animate_display();
			},2000);

		}
	}
	
			//jQuery('#arrivals_inner_wrapper').isotope( 'reLayout', function() {
	
	function process_removals() {
		if (removal_queue.length > 0) {
			var id = removal_queue.shift();
			transitBoardByLine.isotope_container.isotope( 'remove', jQuery("tbody#"+id).parent() );
			process_removals();
		} else {
			process_insertions();
		}
	}
	

	// see if we need to delete any elements
	jQuery("tbody.trip").each(function(index,element){
		var id = jQuery(element).attr("id");
		if ( trip_objects[id] == null && !id.match(/car2go/) ) {
			removal_queue.push(id);
		}
	});
	
	// update or add items
	for(var id in trip_objects) {
		if (jQuery("#"+id).length > 0) {
			if (id.indexOf('spacer') == -1) {
				// update
				if (jQuery("tbody#"+id+" td.arrivals").html() != trip_arrivals_html[id]) {
					jQuery("tbody#"+id+" td.arrivals").html(trip_arrivals_html[id]);
				}
			}
		} else {
			// add it
			insertion_queue.push(trip_objects[id]);
		}
	}
	
	
	
	// create/update car2go tables
	if (transitBoardByLine.car2go > 0) {
		var vehicles = transitBoardByLine.cars.get_vehicles();
		for (i=0; i<transitBoardByLine.car2go; i++) {
			if ( typeof vehicles[i] !== 'undefined') {
				var value = vehicles[i];
				var dist = value[1];
				if (dist < 0.1) {
					dist = 0.1;
				}
				var address = value[0];
				address = '<span class="terminus">'+address.replace("(","</span><br>(");
				if (address.indexOf("/span") == -1) {
					address = address + "</span>";
				}
				if (jQuery("#car2go"+i).length == 0) {
					var car = '\
							<table class="trip_wrapper isotope-item" data-sortkey="90000">\
								<tbody id="car2go'+i+'" class="trip service_color_car2go">\
									<tr valign="middle">\
										<td class="route"><img src="assets/images/car2go/car2go_vehicle.jpg"></td>\
										<td class="destination"><div>Car2Go - '+address+'</div></td>\
										<td class="arrivals">'+dist.toFixed(1)+' mi</td>\
									</tr>\
								</tbody>\
							</table>\
					';
					transitBoardByLine.isotope_container.isotope( 'insert', jQuery(car) );
				} else {
					jQuery('#car2go'+i+' .destination div').html("Car2Go - "+address);
					jQuery('#car2go'+i+' .arrivals').html(dist.toFixed(1)+' mi');
				}
			}
		}
	}
	
	process_removals();
	
	transitBoardByLine.connection_health = data.connectionHealth;
	
	// set time 
        // Don't just use new Date() because time zone may be set wrong
	var client_time = localTime();
    var client_time_formatted = client_time.toString('h:mmtt');
    
	client_time_formatted = client_time_formatted.replace(/^0:/,'12:');
	jQuery('#tb_clock').html(client_time_formatted);
	
}

function filter_queue(arrivalsQueue) {
					
	var now = localTime();
	now = now.getTime(); //milliseconds since epoch				
	
	var tmp_queue = [];
	// removes everything before now and greater than 24 hours from now
	// also filter downtown only if desired
	
	for (var i = 0; i < arrivalsQueue.length; i++) {
		var milliseconds_until_arrival = arrivalsQueue[i].arrivalTime - now;
		if (milliseconds_until_arrival >= 0 && milliseconds_until_arrival <= 24*60*60*1000) {
			if ( arrivalsQueue[i].headsign.match(/downtown only/i) == null || !transitBoardByLine.suppress_downtown_only ) {
				tmp_queue.push(arrivalsQueue[i]);
			}
		}
	}

	// split rows into <= 60 min and > 60 min
	var next_hour = [];
	var later = [];
	for (var i = 0; i < tmp_queue.length; i++) {
		var milliseconds_until_arrival = tmp_queue[i].arrivalTime - now;
		if (milliseconds_until_arrival <= transitBoardByLine.minutes_limit*60*1000) {
			next_hour.push(tmp_queue[i]);
		} else {
			later.push(tmp_queue[i]);
		}
	}

	if (next_hour.length > 0) {
		return next_hour;
	} else {
		return later;
	}

}



				